#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <pthread.h>
#include "table.h"
#include <semaphore.h>


void yield()
{
	sched_yield();
}

struct table_s 
{
	unsigned int _size;
	unsigned int _allocated;
	object** _table;
	pthread_mutex_t mutex;
	sem_t vacio;
        sem_t lleno;


};


table
table_create(const unsigned int size) 
{
	table t = NULL;
	assert(0<size);

	t = (table)calloc(1, sizeof(struct table_s));
	if (t!=NULL) 
	{
		unsigned int i = 0;
		t->_size = size;
		t->_allocated = 0;
		t->_table = calloc(size, sizeof(object*));
		if (t->_table!=NULL) 
		{
			for(i=0; i<size; i++) 
			{
				t->_table[i] = NULL;
			}
		} 
		else 
		{
			free(t); t = NULL;
			perror("table_create, malloc _table:");
		}
	} 
	else 
	{
		perror("table_create, malloc table:");
	}
	pthread_mutex_init(&(t->mutex), NULL);
	sem_init(&(t->vacio), 0, size);
    	sem_init(&(t->lleno), 0, 0);
	return(t);
}


table
table_destroy(table t) 
{
	assert(t!=NULL);
	assert(t->_table!=NULL);
	free(t->_table);
	free(t);
	pthread_mutex_destroy(&(t->mutex));
	sem_destroy(&(t->vacio));
    	sem_destroy(&(t->lleno));
	return ((table)NULL);
}


unsigned int
table_size(const table t) 
{
	assert(t!=NULL);
	yield();
	return(t->_size);
}


unsigned int
table_allocated(table t)
{
assert(t!=NULL);
yield();
  return(t->_allocated);
}


unsigned int
table_alloc(table t, object *o) 
{
	bool found = false;
	unsigned int i = 0;
	assert(t!=NULL);
	assert(t->_table!=NULL);
	assert(o!=NULL);
	yield();

	i = 0;
	#ifdef __DEBUG
	sem_wait(&(t->vacio));
	#endif
	while (!found && i<table_size(t)) 
	{
		yield();
		#ifdef __DEBUG
		pthread_mutex_lock(&(t->mutex));/**/
		#endif
		if (t->_table[i]==NULL) 
		{
			(t->_table)[i] = o;
			found = true;
			
		} 
		else 
		{
			i++;
			
		}
		yield();
		if (found) 
		{
			
			t->_allocated++;
			yield();
			assert(table_allocated(t)<=table_size(t));/**/
			#ifdef __DEBUG
			pthread_mutex_unlock(&(t->mutex));
			sem_post(&(t->lleno));
			#endif
			
			return(i);
		} 
		else 
		{
			#ifdef __DEBUG
			pthread_mutex_unlock(&(t->mutex));
			
			#endif
			
		}
	}
	#ifdef __DEBUG
	sem_post(&(t->lleno));
	#endif
	return(table_size(t));
}


bool
table_is_def(const table t, const unsigned int index) 
{
	assert(t!=NULL);
	assert(t->_table!=NULL);
	assert(index<table_size(t));
	yield();
	return(t->_table[index]!=NULL);
}


object *
table_get(const table t, const unsigned int index) 
{
	assert(t!=NULL);
	assert(t->_table!=NULL);
	yield();
	assert(index<table_size(t));
	
	return(t->_table[index]);
}


void
table_release(table t, const unsigned int index) 
{
	assert(t!=NULL);
	assert(t->_table!=NULL);
	yield();
	assert(index<table_size(t));
	assert(t->_table[index]!=NULL);/*condicion de carrera solucionado con el mutex de abajo*/

	t->_table[index] = NULL;

	yield();
	assert(0<table_allocated(t));/*condicion de carrera */
	yield();
	t->_allocated--;

}

bool
table_getted (table tbl, int idx)
{
	#ifdef __DEBUG
	sem_wait(&(tbl->lleno));
	pthread_mutex_lock(&tbl->mutex);
	#endif
	if (table_is_def(tbl, idx)) 
	{
		
		yield();
		table_release(tbl, idx);
		#ifdef __DEBUG
		pthread_mutex_unlock(&tbl->mutex);
		sem_post(&(tbl->vacio));
		#endif 
		return (1);
	}
	#ifdef __DEBUG
	pthread_mutex_unlock(&tbl->mutex);
	sem_post(&(tbl->vacio));
	#endif
	return (0);
}
